//
// callcc.c
// creation and calling of continuations
//
// NOTE: these hacks make use of the frame pointer, so they must
// be compiled -fno-omit-frame-pointer!
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#include <stdio.h>
#include "potion.h"
#include "internal.h"

// 恢复 PNCont self 中记录的系统栈和相关寄存器数据，返回 self 。
PN_MSG(potion_continuation_yield) {
  struct PNCont *cc = (struct PNCont *)self;
  PN *start, *end, *sp1 = P->mem->cstack;
  #if POTION_STACK_DIR > 0
    start = (PN *)cc->stack[0];
    end = (PN *)cc->stack[1];
  #else
    start = (PN *)cc->stack[1];
    end = (PN *)cc->stack[0];
  #endif

  if ((PN)sp1 != cc->stack[0]) {
    fprintf(stderr, "** TODO: continuations which switch stacks must be rewritten. (%p != %p)\n",
      sp1, (void *)(cc->stack[0]));
    return PN_NIL;
  }

  //
  // move stack pointer, fill in stack, resume
  // TODO hsq 用于在汇编中取之作为本函数的返回值，取后
  cc->stack[3] = self;
  #ifdef POTION_X86
    #if __WORDSIZE == 64
      // TODO hsq 寄存器“bp”具有固定用途，不能在 asm 语句中强制改写C/C++(1131)
      __asm__ ("mov 0x8(%2), %%rsp;"
              "mov 0x10(%2), %%rbp;"
              "mov %2, %%rbx;"
              // 0x48 是 9 个 PN ，即 4 + PN_SAVED_REGS
              "add $0x48, %2;"
            "loop:"
              "mov (%2), %%rax;"
              "add $0x8, %0;"
              "mov %%rax, (%0);"
              "add $0x8, %2;"
              "cmp %0, %1;"
              "jne loop;"
              "mov 0x18(%%rbx), %%rax;"
              "movq $0x0, 0x18(%%rbx);"
              "mov 0x28(%%rbx), %%r12;"
              "mov 0x30(%%rbx), %%r13;"
              "mov 0x38(%%rbx), %%r14;"
              "mov 0x40(%%rbx), %%r15;"
              "mov 0x20(%%rbx), %%rbx;"
              "leave; ret"
              :/* no output */
              :"r"(start), "r"(end), "r"(cc->stack)
              #ifdef __TINYC__
              :"rax", "rsp", "rbp", "rbx"
              #else
              /* TODO hsq Linux 报错: bp cannot be used in asm here */
              :"%rax", "%rsp"/* , "%rbp" */, "%rbx"
              #endif
              );
    #endif
  #else
    fprintf(stderr, "** TODO: callcc does not work outside of X86.\n");
  #endif
  // 没运行到这里，在汇编中就返回了。
  return self;
}

// 创建 PNCont 对象，记录系统栈和相关寄存器的数据。
PN_MSG(potion_callcc) {
  struct PNCont *cc;
  PN_SIZE n;
  PN *start, *end, *sp1 = P->mem->cstack, *sp2, *sp3;
  POTION_ESP(&sp2);
  POTION_EBP(&sp3);
  #if POTION_STACK_DIR > 0
    n = sp2 - sp1;
    start = sp1;
    end = sp2;
  #else
    n = sp1 - sp2 + 1;
    start = sp2;
    end = sp1;
  #endif

  cc = PN_ALLOC_N(PN_TCONT, struct PNCont, sizeof(PN) * (n + 3 + PN_SAVED_REGS));
  cc->len = n + 3;
  cc->stack[0] = (PN)sp1;
  cc->stack[1] = (PN)sp2;
  cc->stack[2] = (PN)sp3;
  cc->stack[3] = PN_NIL;
  // 与 PN_SAVED_REGS 对应
  #ifdef POTION_X86
    #if __WORDSIZE == 64
      __asm__ ("mov %%rbx, 0x20(%0);"
              "mov %%r12, 0x28(%0);"
              "mov %%r13, 0x30(%0);"
              "mov %%r14, 0x38(%0);"
              "mov %%r15, 0x40(%0);"::"r"(cc->stack));
    #endif
  #endif
  // TODO hsq 有必要拷贝整个系统栈？
  PN_MEMCPY_N((char *)(cc->stack + 4 + PN_SAVED_REGS), start + 1, PN, n - 1);
  return (PN)cc;
}

// cont 类型的 string 消息闭包。
PN_MSG(potion_continuation_string) {
  return PN_LITERAL("Continuation");
}

// 设置虚表 cont 的 .call 闭包为 yield ；为其注册 string 消息。
void potion_cont_init(Potion *P) {
  PN_METHOD(P->lobby, "here", potion_callcc, 0);

  PN cnt_vt = PN_VTABLE(PN_TCONT);
  potion_type_call_is(cnt_vt, PN_FUNC(potion_continuation_yield, 0));
  PN_METHOD(cnt_vt, "string", potion_continuation_string, 0);
}
